package cn.zwx.redisson.distributed.lock.example.utils;

import cn.zwx.redisson.distributed.lock.example.annotation.Lock;
import cn.zwx.redisson.distributed.lock.example.enums.LockModelEnum;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.RedissonMultiLock;
import org.redisson.RedissonRedLock;
import org.redisson.api.RLock;
import org.redisson.api.RReadWriteLock;
import org.redisson.api.RedissonClient;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;

import java.util.ArrayList;
import java.util.List;

/**
 * @author zhangwenxue
 */
public class LockUtil {

    public static RLock getLock(ProceedingJoinPoint proceedingJoinPoint, Lock lock, LockModelEnum lockModel, RedissonClient redissonClient) {
        String[] keys = lock.keys();
        if (keys.length == 0) {
            throw new RuntimeException("keys不能为空");
        }
        // 获取所有参数名称
        String[] parameterNames = new LocalVariableTableParameterNameDiscoverer().getParameterNames(((MethodSignature) proceedingJoinPoint.getSignature()).getMethod());
        // 获取参数列表
        Object[] args = proceedingJoinPoint.getArgs();
        RLock rLock;
        //创建锁对象.
        switch (lockModel) {
            case FAIR:
                rLock = redissonClient.getFairLock(SpelUtils.getValeBySpel(keys[0],parameterNames,args,lock.keyExtension()).get(0));
                break;
            case RED_LOCK:
                List<RLock> rLocks=new ArrayList<>();
                for (String key : keys) {
                    List<String> vauleBySpel = SpelUtils.getValeBySpel(key, parameterNames, args, lock.keyExtension());
                    for (String s : vauleBySpel) {
                        rLocks.add(redissonClient.getLock(s));
                    }
                }
                RLock[] locks =new RLock[rLocks.size()];
                int index=0;
                for (RLock r : rLocks) {
                    locks[index++]=r;
                }
                rLock = new RedissonRedLock(locks);
                break;
            case MULTIPLE:
                rLocks=new ArrayList<>();

                for (String key : keys) {
                    List<String> vauleBySpel = SpelUtils.getValeBySpel(key, parameterNames, args, lock.keyExtension());
                    for (String s : vauleBySpel) {
                        rLocks.add(redissonClient.getLock(s));
                    }
                }
                locks=new RLock[rLocks.size()];
                index=0;
                for (RLock r : rLocks) {
                    locks[index++]=r;
                }
                rLock = new RedissonMultiLock(locks);
                break;
            case REENTRANT:
                List<String> vauleBySpel = SpelUtils.getValeBySpel(keys[0], parameterNames, args, lock.keyExtension());
                //如果spel表达式是数组或者LIST 则使用红锁
                if(vauleBySpel.size()==1){
                    rLock= redissonClient.getLock(vauleBySpel.get(0));
                }else {
                    locks=new RLock[vauleBySpel.size()];
                    index=0;
                    for (String s : vauleBySpel) {
                        locks[index++]=redissonClient.getLock(s);
                    }
                    rLock = new RedissonRedLock(locks);
                }
                break;
            case READ:
                RReadWriteLock rReadWriteLock = redissonClient.getReadWriteLock(SpelUtils.getValeBySpel(keys[0],parameterNames,args, lock.keyExtension()).get(0));
                rLock = rReadWriteLock.readLock();
                break;
            case WRITE:
                RReadWriteLock readWriteLock = redissonClient.getReadWriteLock(SpelUtils.getValeBySpel(keys[0],parameterNames,args, lock.keyExtension()).get(0));
                rLock = readWriteLock.writeLock();
                break;
            default:
                throw new RuntimeException("目前不支持锁类型");
        }
        return rLock;
    }


    public static LockModelEnum getAutoLockModel(Lock lock, String[] keys) {
        LockModelEnum lockModel = lock.lockModel();
        if (lockModel.equals(LockModelEnum.AUTO)) {
            if (keys.length > 1) {
                lockModel = LockModelEnum.RED_LOCK;
            } else {
                lockModel = LockModelEnum.REENTRANT;
            }
        }
        return lockModel;
    }
}
